using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

using Pegasus.Runtime.InteropServices;
using Pegasus.Windows.API;
using Pegasus.Windows.API.Enumerations;
using Pegasus.Windows.API.Interfaces;

namespace Pegasus.Windows.Shell
{
	/// <summary>
	/// This represents the Windows Shell.
	/// </summary>
	/// <remarks>
	/// </remarks>
	public static class WindowsShell
	{
		// Local Static Values
		private static IShellFolder s_rootShellFolder = null;
		private static DesktopItem s_desktop = null;
		private static Dictionary<ImageSize, ImageListInfo> m_systemImageLists = new Dictionary<ImageSize, ImageListInfo>();

		/// <summary>
		/// Gets the desktop item, the root of the shell namespace.
		/// </summary>
		/// <value>The desktop.</value>
		public static DesktopItem Desktop
		{
			get
			{
				if( s_desktop == null )
				{
					s_desktop = new DesktopItem();
				}

				return s_desktop;
			}
		}

		/// <summary>
		/// Gets the root shell folder.
		/// </summary>
		/// <value>The root shell folder.</value>
		internal static IShellFolder RootShellFolder
		{
			get
			{
				if( s_rootShellFolder == null )
				{
					Shell32.SHGetDesktopFolder( out s_rootShellFolder );
				}

				return s_rootShellFolder;
			}
		}

		/// <summary>
		/// Gets the system image list.
		/// </summary>
		/// <param name="size">The size.</param>
		/// <returns></returns>
		public static ImageList GetSystemImageList( ImageSize size )
		{
			if( m_systemImageLists.ContainsKey( size ) )
			{
				return m_systemImageLists[ size ].NetImageList;
			}

			return CreateSystemImageList( size );
		}

		/// <summary>
		/// News the shell item.
		/// </summary>
		/// <param name="item">The item.</param>
		internal static void NewShellItem( ShellItem item )
		{
			foreach( ImageSize size in Enum.GetValues( typeof( ImageSize ) ) )
			{
				if( m_systemImageLists.ContainsKey( size ) )
				{
					ImageListInfo imageListInfo = m_systemImageLists[ size ];
					if( imageListInfo.NetImageList.Images.Count <= item.ImageIndex )
					{
						UpdateImageList( imageListInfo );
					}
				}
			}
		}

		/// <summary>
		/// Creates the system image list.
		/// </summary>
		/// <param name="size">The size.</param>
		/// <returns></returns>
		private static ImageList CreateSystemImageList( ImageSize size )
		{
			IImageList sysImageList = null;
			ImageList netImageList = new ImageList();
			netImageList.ColorDepth = ColorDepth.Depth32Bit;

			switch( size )
			{
				case ImageSize.SMALL:
					sysImageList = GetIImageList( SHIL.SMALL );
					netImageList.ImageSize = new Size( 16, 16 );
					break;

				case ImageSize.MEDIUM:
					sysImageList = GetIImageList( SHIL.LARGE );
					netImageList.ImageSize = new Size( 24, 24 );
					break;

				case ImageSize.LARGE:
					sysImageList = GetIImageList( SHIL.LARGE );
					netImageList.ImageSize = new Size( 32, 32 );
					break;

				case ImageSize.EXTRA_LARGE:
					sysImageList = GetIImageList( SHIL.EXTRALARGE );
					netImageList.ImageSize = new Size( 48, 48 );
					break;

				case ImageSize.JUMBO:
					sysImageList = GetIImageList( SHIL.JUMBO );
					netImageList.ImageSize = new Size( 256, 256 );
					break;

				default:
					throw new ArgumentException( "Unknown image size", "size" );
			}


			ImageListInfo imageListInfo = new ImageListInfo( sysImageList, netImageList );
			m_systemImageLists.Add( size, imageListInfo );

			UpdateImageList( imageListInfo );

			return imageListInfo.NetImageList;
		}

		/// <summary>
		/// Gets the I image list.
		/// </summary>
		/// <param name="imageListSize">Size of the image list.</param>
		/// <returns></returns>
		private static IImageList GetIImageList( SHIL imageListSize )
		{
			IntPtr ipImageList = IntPtr.Zero;

			Shell32.SHGetImageList( imageListSize, MarshalEx.GetGuid( typeof( IImageList ) ), ref ipImageList );
			IImageList imageList = (IImageList) Marshal.GetTypedObjectForIUnknown( ipImageList, typeof( IImageList ) );

			return imageList;
		}

		/// <summary>
		/// Updates the image list.
		/// </summary>
		/// <param name="imageListInfo">The image list info.</param>
		private static void UpdateImageList( ImageListInfo imageListInfo )
		{
			IImageList sysImageList = imageListInfo.SystemImageList;
			ImageList netImageList = imageListInfo.NetImageList;

			int count = 0;
			imageListInfo.SystemImageList.GetImageCount( ref count );

			netImageList.Images.Clear();
			for( int x = 0; x < count; x++ )
			{
				IntPtr hIcon = sysImageList.GetIcon( x, ILD.NORMAL );
				netImageList.Images.Add( Icon.FromHandle( hIcon ) );
			}
		}

		/// <summary>
		/// Holds information about an image list.
		/// </summary>
		private class ImageListInfo
		{
			// Public Instance Data
			public IImageList SystemImageList;
			public ImageList NetImageList;

			/// <summary>
			/// Initializes a new instance of the <see cref="ImageListInfo"/> class.
			/// </summary>
			/// <param name="systemImageList">The system image list.</param>
			/// <param name="netImageList">The net image list.</param>
			public ImageListInfo( IImageList systemImageList, ImageList netImageList )
			{
				SystemImageList = systemImageList;
				NetImageList = netImageList;
			}
		}
	}
}
